package Sintatico;

import java.util.ArrayList;
import java.util.Stack;

import Utils.Gramatica;
import Utils.PrintCollor;
import Utils.Producao;
import Utils.Tabela;
import Utils.TipoProducao;
import Utils.Token;
import org.fusesource.jansi.Ansi;

/**
 *
 * @author Adriano
 */
public class AnalisadorSintatico {

    private Gramatica gramatica;
    private Tabela tabela;
    private Stack<TipoProducao> pilha;
    private ArrayList<Token> buffer;
    private int indexBuff;
    private boolean isRun;

    AnalisadorSintatico(ArrayList<Token> tokens, int index) {
        this.indexBuff = index;
        this.isRun = false;
        this.gramatica = new Gramatica();
        this.tabela = new Tabela();
        this.pilha = new Stack<>();
        this.buffer = tokens;
    }

    public void analisar() {
        this.isRun = true;
        this.pilha.clear();
        this.pilha.push(TipoProducao.BOTTON);
        PrintCollor.print(Ansi.Color.BLUE, "**** EXPRESS *** ");
        printPilha();
        while (this.isRun) {
            Token token = getToken();
            if (this.pilha.peek() == TipoProducao.BOTTON) {
                PrintCollor.print(Ansi.Color.BLUE, "SUCESSO!");
            } else if ((this.pilha.peek() == TipoProducao.BOTTON)
                    && (token.getTipo() != TipoProducao.FIM)) {
                shift(token);
            } else {
                switch (this.tabela
                        .getValor(this.pilha.peek(), token.getTipo())) {
                    case IGUAL:
                    case MENOR:
                        shift(token);
                        break;
                    case MAIOR:
                        reduce(token);
                        break;
                    case ERRO:
                    default:
                        erro(token);
                }

            }

            if (this.isRun) {
                printPilha();
            }
        }

    }

    private void shift(Token token) {
        this.pilha.push(token.getTipo());
        if (this.buffer.size() > this.indexBuff) {
            this.indexBuff++;
        }
        System.out.println("SHIFT \n");
    }

    private void reduce(Token token) {
        ArrayList<TipoProducao> produzido = new ArrayList<>();
        Producao p = null;
        for (int i = this.pilha.size() - 1; i > -1; i--) {
            produzido.add(0, this.pilha.pop());
            p = this.gramatica.getByProduto(produzido);
            if (p != null) {
                this.pilha.push(p.getProducao());
                break;
            }
        }

        if (p == null) {
            for (TipoProducao tp : produzido) {
                this.pilha.push(tp);
            }
        }
        System.out.println("REDUCE \n");
    }

    private void erro(Token token) {
        System.out.println("Erro " + token);
        this.isRun = false;
    }

    private Token getToken() {
        if (this.buffer.size() == this.indexBuff) {
            return new Token("FINAL DE ARQUIVO", "$", TipoProducao.FIM, 0, 0);
        }
        return this.buffer.get(this.indexBuff);
    }

    public void printPilha() {
        System.out.println("*** Valor da Pilha *** " + this.pilha.size());
        for (int i = 0; i < this.pilha.size(); i++) {
            System.out.println("Elemento: " + this.pilha.get(i));
        }

        System.out.println("****** ");
    }

}
